/*
	message_handling.c

	contains kernel-level IPC message passing functionality

	Author: Aidan Goddard 21/8/13
*/

#include"../setup/headers/printf.h"
#include"headers/kernel_tables.h"
#include"headers/bakery_lock.h"
#include"headers/scheduler_functions.h"

// setup function
void SetupMessageHandler(void)
{
	/*
	printf("\n%u", sizeof(MSG_CONTROL));
	printf("\n0x%x", sizeof(MSG_CONTROL_MAIN));
	printf("\n%u", sizeof(MSG));
	printf("\n%u", sizeof(MSG_BUFFER));
	*/

	// zero the necessary fields
	// zero buffer controls
	int i;
	for(i = 0; i < 512; i++)
	{
		msg_control->buffer_controls[i].server_ID = 0;
		msg_control->buffer_controls[i].client_ID = 0;
		msg_control->buffer_controls[i].channel_ID = 0;
		msg_control->buffer_controls[i].server_message_count = 0;
		msg_control->buffer_controls[i].server_queue_start = 0;
		msg_control->buffer_controls[i].server_queue_last = 0;
		msg_control->buffer_controls[i].client_message_count = 0;
		msg_control->buffer_controls[i].client_queue_start = 0;
		msg_control->buffer_controls[i].client_queue_last = 0;
		msg_control->buffer_controls[i].client_read_waiting_TID = 0;
		msg_control->buffer_controls[i].client_write_sync_waiting_TID = 0;
		msg_control->buffer_controls[i].server_read_waiting_TID = 0;
		msg_control->buffer_controls[i].server_write_sync_waiting_TID = 0;

		// zero locks
		int k;
		for(k = 0; k < 32; k++)
		{
			msg_control->buffer_controls[i].lock.entering[k] = 0;
			msg_control->buffer_controls[i].lock.ticket[k] = 0;
		}

		// set up allocation stack
		// stack just contains index numbers of the buffers
		msg_control->buffer_stack[i] = i;
	}

	// set stack pointer to 0
	msg_control->buffer_stack_index = 0;

	// zero the stack lock
	for(i = 0; i < 32; i++)
	{
		msg_control->stack_lock.entering[i] = 0;
		msg_control->stack_lock.ticket[i] = 0;
	}

	return;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////// HELPER FUNCTIONS ////////////////////////////////////////////////////////////////

// basic hash function to use
// implements the DJB hash found here: http://www.partow.net/programming/hashfunctions/#Download
static uint64_t HashMe(char *str, uint32_t magic_int)
{
	// first get the length of the string (should be null-terminated - stop at 2048 chars)
	int len;
	for(len = 0; len < 2048; len++)
	{
		if(str[len] == 0)
			break;
	}
	len++;

	// have length of string (defaults to 2048 chars if string is not null-terminated)
	// now hash it (DJB hash)
	uint64_t hash = 5381;
	uint64_t i = 0;

	for(i = 0; i < len; str++, i++)
	{
		hash = ((hash << 5) + hash) + (*str);
	}

	// hash the magic int
	hash = ((hash << 5) + hash) + (magic_int & 0xff);
	hash = ((hash << 5) + hash) + ((magic_int >> 8) & 0xff);
	hash = ((hash << 5) + hash) + ((magic_int >> 16) & 0xff);
	hash = ((hash << 5) + hash) + ((magic_int >> 24) & 0xff);

	// check hash for zero
	if(hash == 0)
	{
		hash = 1;
	}

	// return the hash
	return hash;
}




////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////// THE FOLLOWING FUNCTIONS ASSUME INTERUPTS ARE ENABLED ///////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// function to allocate a channel, returns channel ID or 0 on error
MSG_CHANNEL	AllocateChannel(char *magic_string, uint32_t magic_int)
{
	// first generate a hash of the magic string and int
	uint64_t hash = HashMe(magic_string, magic_int);

	// shift it left 16 bits
	hash = hash << 16;

	// grab lock on the stack
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&(msg_control->stack_lock), CPU);

	// then grab the index of the next available channel if stack is not empty
	if(msg_control->buffer_stack_index == 512)
	{
		BakeryLeave(&(msg_control->stack_lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 0;
	}

	int channel = msg_control->buffer_stack[msg_control->buffer_stack_index];
	msg_control->buffer_stack_index++;

	// release lock
	BakeryLeave(&(msg_control->stack_lock), CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");

	// use the channel number and hash to make the channel ID
	uint64_t channel_ID = hash + (channel & 0xffff);

	// now set server-side values on the channel (bind it)
	msg_control->buffer_controls[channel].channel_ID = channel_ID;
	msg_control->buffer_controls[channel].server_ID = GetCurrentTID() >> 8;

	// zero the rest of them
	msg_control->buffer_controls[channel].client_ID = 0;
	msg_control->buffer_controls[channel].server_message_count = 0;
	msg_control->buffer_controls[channel].server_queue_start = 0;
	msg_control->buffer_controls[channel].server_queue_last = 0;
	msg_control->buffer_controls[channel].client_message_count = 0;
	msg_control->buffer_controls[channel].client_queue_start = 0;
	msg_control->buffer_controls[channel].client_queue_last = 0;
	msg_control->buffer_controls[channel].client_read_waiting_TID = 0;
	msg_control->buffer_controls[channel].client_write_sync_waiting_TID = 0;
	msg_control->buffer_controls[channel].server_read_waiting_TID = 0;
	msg_control->buffer_controls[channel].server_write_sync_waiting_TID = 0;

	// finished, now return the channel ID
	return channel_ID;
}

// function to deallocate a channel, will wake any threads currently waiting on it
// return 0 on success, 1 for channel does not exist, 2 for calling process is not the server process
uint32_t DeallocateChannel(MSG_CHANNEL channel)
{
	// get channel index
	int index = channel & 0xffff;

	// get lock on the channel
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&(msg_control->buffer_controls[index].lock), CPU);

	// check channel index is correct
	if(msg_control->buffer_controls[index].channel_ID != channel)
	{
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 1;
	}

	// check if the calling process is the server process (only the server may terminate the channel)
	uint64_t calling_PID = GetCurrentTID() >> 8;
	if(msg_control->buffer_controls[index].server_ID != calling_PID)
	{
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 2;
	}

	// zero server and client buffers
	msg_control->buffer_controls[index].client_message_count = 0;
	msg_control->buffer_controls[index].client_queue_last = 0;
	msg_control->buffer_controls[index].client_queue_start = 0;
	msg_control->buffer_controls[index].server_message_count = 0;
	msg_control->buffer_controls[index].server_queue_last = 0;
	msg_control->buffer_controls[index].server_queue_start = 0;

	// check each waiting value and wake the threads if they are non-zero
	if(msg_control->buffer_controls[index].client_read_waiting_TID != 0)
	{
		// wake the thread (don't need to check the return value)
		uint64_t TID_to_wake = msg_control->buffer_controls[index].client_read_waiting_TID;
		msg_control->buffer_controls[index].client_read_waiting_TID = 0;
		WakeThreadEx(TID_to_wake);
	}
	if(msg_control->buffer_controls[index].client_write_sync_waiting_TID != 0)
	{
		// wake the thread (don't need to check the return value)
		uint64_t TID_to_wake = msg_control->buffer_controls[index].client_write_sync_waiting_TID;
		msg_control->buffer_controls[index].client_write_sync_waiting_TID = 0;
		WakeThreadEx(TID_to_wake);
	}
	if(msg_control->buffer_controls[index].server_read_waiting_TID != 0)
	{
		// wake the thread (don't need to check the return value)
		uint64_t TID_to_wake = msg_control->buffer_controls[index].server_read_waiting_TID;
		msg_control->buffer_controls[index].server_read_waiting_TID = 0;
		WakeThreadEx(TID_to_wake);
	}
	if(msg_control->buffer_controls[index].server_write_sync_waiting_TID != 0)
	{
		// wake the thread (don't need to check the return value)
		uint64_t TID_to_wake = msg_control->buffer_controls[index].server_write_sync_waiting_TID;
		msg_control->buffer_controls[index].server_write_sync_waiting_TID = 0;
		WakeThreadEx(TID_to_wake);
	}

	// zero owner IDs and channel ID
	msg_control->buffer_controls[index].channel_ID = 0;
	msg_control->buffer_controls[index].client_ID = 0;
	msg_control->buffer_controls[index].server_ID = 0;

	// unlock buffer controls
	BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);

	// place buffer on stack
	BakeryEnter(&(msg_control->stack_lock), CPU);

	msg_control->buffer_stack_index--;
	msg_control->buffer_stack[msg_control->buffer_stack_index] = index;

	// release lock
	BakeryLeave(&(msg_control->stack_lock), CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");

	// finished, return 0
	return 0;
}

// function to find a channel ID
// returns the channel ID if found or 0 if not found
MSG_CHANNEL FindChannel(char *magic_string, uint32_t magic_int)
{
	// first generate a hash of the magic string and int
	uint64_t hash = HashMe(magic_string, magic_int);

	// shift it left 16 bits
	hash = hash << 16;

	// now scan through each channel to check if the IDs match
	uint64_t channel = 0;
	int i;
	for(i = 0; i < 512; i++)
	{
		if((msg_control->buffer_controls[i].channel_ID & 0xffffffffffff0000) == hash)
		{
			channel = hash + i;
			break;
		}
	}

	return channel;
}

// function to bind the client process/kernel module to the channel
// returns 0 on success, 1 if given channel ID is not valid, 2 if channel is already bound
uint32_t ClientBindChannel(uint64_t PID, MSG_CHANNEL channel)
{
	// get channel index from channel
	int index = channel & 0xffff;

	// get lock on the channel
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&(msg_control->buffer_controls[index].lock), CPU);

	// check channel index is correct
	if(msg_control->buffer_controls[index].channel_ID != channel)
	{
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 1;
	}

	// channel index is correct, check if channel is already bound
	if(msg_control->buffer_controls[index].client_ID != 0)
	{
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 2;
	}

	// channel ID is valid, channel is not already bound.
	// set cleint ID
	msg_control->buffer_controls[index].client_ID = PID;

	// finished, return no error
	BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");
	return 0;
}

// function for the server to read a message
// returns 0 on success, 1 on invalid channel, 2 if thread already reading channel, 3 if thread was woken while waiting by something other than the message system,
// 4 if there appears to be contention between this thread and another over the channel, 5 if the calling process is not the same as the creating process.
uint32_t ServerRead(MSG_CHANNEL channel, uint8_t *buffer)
{
	// get channel index
	int index = channel & 0xffff;

	// aquire lock on the channel
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&(msg_control->buffer_controls[index].lock), CPU);

	// check channel exists
	// if not, error out
	if(msg_control->buffer_controls[index].channel_ID != channel)
	{
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 1;
	}

	// check the calling PID matches the server PID
	uint64_t caller_PID = GetCurrentTID() >> 8;
	if(caller_PID != msg_control->buffer_controls[index].server_ID)
	{
		// error out
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 5;
	}

	// then check if there is already a server thread waiting on a message in this buffer
	// error out if there is (only 1 thread may be waiting for a message on each buffer)
	if(msg_control->buffer_controls[index].server_read_waiting_TID != 0)
	{
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 2;
	}

	// get server message count
	// if zero, wait until message comes in before continuing
	int msg_count = msg_control->buffer_controls[index].server_message_count;
	if(msg_count == 0)
	{
		// set this thread as the one to sleep
		msg_control->buffer_controls[index].server_read_waiting_TID = GetCurrentTID();

		// release lock
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");

		// wait for thread queue to be non-empty
		WaitMe();

		// after waking, update CPU ID and claim lock
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("cli");
		__asm__ __volatile__("":::"memory");
		CPU = GetCPUID();
		BakeryEnter(&(msg_control->buffer_controls[index].lock), CPU);

		// check if the thread was woken by something else (waiting thread value should be cleared)
		if(msg_control->buffer_controls[index].server_read_waiting_TID != 0)
		{
			// if so, return error
			// release lock
			BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
			__asm__ __volatile__("":::"memory");
			__asm__ __volatile__("sti");
			__asm__ __volatile__("":::"memory");
			return 3;
		}

		// update message count
		msg_count = msg_control->buffer_controls[index].server_message_count;

		// check that message count is non-zero
		if(msg_count == 0)
		{
			// release lock
			BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
			__asm__ __volatile__("":::"memory");
			__asm__ __volatile__("sti");
			__asm__ __volatile__("":::"memory");
			return 4;
		}
	}

	// messages exist in the queue to be read
	// get queue start and end (beware of 64 entry wrap-around)
	int msg_start = msg_control->buffer_controls[index].server_queue_start;

	// get a pointer to the 256 byte message at the start of the queue and the destination buffer
	uint64_t *message = (uint64_t*)(&(msg_buffers[index].server_queue[msg_start]));
	uint64_t *dest = (uint64_t*)buffer;

	// update message queue pointers
	msg_start++;
	if(msg_start == 64)
	{
		msg_start = 0;
	}
	msg_count--;
	msg_control->buffer_controls[index].server_queue_start = msg_start;
	msg_control->buffer_controls[index].server_message_count = msg_count;

	// copy the message to the destination buffer
	int i;
	for(i = 0; i < 32; i++)
	{
		dest[i] = message[i];
	}

	// check if there is a thread waiting on delivery
	if(msg_control->buffer_controls[index].server_write_sync_waiting_TID != 0)
	{
		// wake that thread
		uint64_t t = msg_control->buffer_controls[index].server_write_sync_waiting_TID;
		msg_control->buffer_controls[index].server_write_sync_waiting_TID = 0;
		WakeThreadEx(t);
	}

	// release lock and return success
	BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");
	return 0;
}

// function to check if there are any messages in the queue
// returns 0 if there are messages, 1 if not, 2 if the given channel is invalid, 3 if calling process is not the creating process.
uint32_t ServerPeek(MSG_CHANNEL channel)
{
	// get channel index
	int index = channel & 0xffff;

	// aquire lock on the channel
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&(msg_control->buffer_controls[index].lock), CPU);

	// check channel exists
	// if not, error out
	if(msg_control->buffer_controls[index].channel_ID != channel)
	{
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 2;
	}

	// check the calling PID matches the server PID
	uint64_t caller_PID = GetCurrentTID() >> 8;
	if(caller_PID != msg_control->buffer_controls[index].server_ID)
	{
		// error out
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 3;
	}

	// get the message count and return it
	int to_return = 1;		// no messages available

	if(msg_control->buffer_controls[index].server_message_count != 0)
	{
		to_return = 0;
	}

	BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");
	return to_return;
}

// function for the client to read a message
// returns 0 on success, 1 on invalid channel, 2 if thread already reading channel, 3 if thread was woken while waiting by something other than the message system,
// 4 if there appears to be contention between this thread and another over the channel, 5 if the calling process is not the same as the client process.
uint32_t ClientRead(MSG_CHANNEL channel, uint8_t *buffer, uint64_t PID)
{
	// get channel index
	int index = channel & 0xffff;

	// aquire lock on the channel
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&(msg_control->buffer_controls[index].lock), CPU);

	// check channel exists
	// if not, error out
	if(msg_control->buffer_controls[index].channel_ID != channel)
	{
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 1;
	}

	// check the calling PID matches the client PID
	if(PID != msg_control->buffer_controls[index].client_ID)
	{
		// error out
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 5;
	}

	// then check if there is already a client thread waiting on a message in this buffer
	// error out if there is (only 1 thread may be waiting for a message on each buffer)
	if(msg_control->buffer_controls[index].client_read_waiting_TID != 0)
	{
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 2;
	}

	// get client message count
	// if zero, wait until message comes in before continuing
	int msg_count = msg_control->buffer_controls[index].client_message_count;
	if(msg_count == 0)
	{
		// set this thread as the one to sleep
		msg_control->buffer_controls[index].client_read_waiting_TID = GetCurrentTID();

		// release lock
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");

		// wait for thread queue to be non-empty
		WaitMe();

		// after waking, update CPU ID and claim lock
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("cli");
		__asm__ __volatile__("":::"memory");
		CPU = GetCPUID();
		BakeryEnter(&(msg_control->buffer_controls[index].lock), CPU);

		// check if the thread was woken by something else (waiting thread value should be cleared)
		if(msg_control->buffer_controls[index].client_read_waiting_TID != 0)
		{
			// if so, return error
			// release lock
			BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
			__asm__ __volatile__("":::"memory");
			__asm__ __volatile__("sti");
			__asm__ __volatile__("":::"memory");
			return 3;
		}

		// update message count
		msg_count = msg_control->buffer_controls[index].client_message_count;

		// check that message count is non-zero
		if(msg_count == 0)
		{
			// release lock
			BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
			__asm__ __volatile__("":::"memory");
			__asm__ __volatile__("sti");
			__asm__ __volatile__("":::"memory");
			return 4;
		}
	}

	// messages exist in the queue to be read
	// get queue start and end (beware of 64 entry wrap-around)
	int msg_start = msg_control->buffer_controls[index].client_queue_start;

	// get a pointer to the 256 byte message at the start of the queue and the destination buffer
	uint64_t *message = (uint64_t*)(&(msg_buffers[index].client_queue[msg_start]));
	uint64_t *dest = (uint64_t*)buffer;

	// update message queue pointers
	msg_start++;
	if(msg_start == 64)
	{
		msg_start = 0;
	}
	msg_count--;
	msg_control->buffer_controls[index].client_queue_start = msg_start;
	msg_control->buffer_controls[index].client_message_count = msg_count;

	// copy the message to the destination buffer
	int i;
	for(i = 0; i < 32; i++)
	{
		dest[i] = message[i];
	}

	// check if there is a thread waiting on delivery
	if(msg_control->buffer_controls[index].client_write_sync_waiting_TID != 0)
	{
		// wake that thread
		uint64_t thread = msg_control->buffer_controls[index].client_write_sync_waiting_TID;
		msg_control->buffer_controls[index].client_write_sync_waiting_TID = 0;
		WakeThreadEx(thread);
	}

	// release lock and return success
	BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");
	return 0;
}

// function to check if there are any messages in the queue
// returns 0 if there are messages, 1 if not, 2 if the given channel is invalid, 3 if calling process is not the client process.
uint32_t ClientPeek(MSG_CHANNEL channel, uint64_t PID)
{
	// get channel index
	int index = channel & 0xffff;

	// aquire lock on the channel
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&(msg_control->buffer_controls[index].lock), CPU);

	// check channel exists
	// if not, error out
	if(msg_control->buffer_controls[index].channel_ID != channel)
	{
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 2;
	}

	// check the calling PID matches the client PID
	if(PID != msg_control->buffer_controls[index].client_ID)
	{
		// error out
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 3;
	}

	// get the message count and return it
	int to_return = 1;		// no messages available

	if(msg_control->buffer_controls[index].client_message_count != 0)
	{
		to_return = 0;
	}

	BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");
	return to_return;
}

// message send functions
// returns 0 on success, 1 if channel is not valid, 2 if the calling PID is not the server, 3 if the buffer is full,
// 4 if there is already a thread waiting on a sync write, 5 if the thread was woken by something else
uint32_t ServerSendSync(MSG_CHANNEL channel, uint8_t *buffer)
{
	// get channel index
	int index = channel & 0xffff;

	// aquire lock on the channel
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&(msg_control->buffer_controls[index].lock), CPU);

	// check channel exists
	// if not, error out
	if(msg_control->buffer_controls[index].channel_ID != channel)
	{
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 1;
	}

	// check the calling PID matches the server PID
	uint64_t PID = GetCurrentTID() >> 8;
	if(PID != msg_control->buffer_controls[index].server_ID)
	{
		// error out
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 2;
	}

	// check if no other thread is currently waiting on sync send
	if(msg_control->buffer_controls[index].client_write_sync_waiting_TID != 0)
	{
		// error out
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 4;
	}

	// get the index into the CLIENTS's receive queue
	int q_index = msg_control->buffer_controls[index].client_queue_start + msg_control->buffer_controls[index].client_message_count;

	// check the buffer is not full
	if(msg_control->buffer_controls[index].client_message_count == 64)
	{
		// error out
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 3;
	}

	// buffer is not full, increase message count
	msg_control->buffer_controls[index].client_message_count++;


	// put the message into the buffer
	uint64_t *input_buffer = (uint64_t*)buffer;
	uint64_t *dest_buffer = (uint64_t*)&(msg_buffers[index].client_queue[q_index].data);

	int i;
	for(i = 0; i < 32; i++)
	{
		dest_buffer[i] = input_buffer[i];
	}

	// check for thread waiting on a message
	if(msg_control->buffer_controls[index].client_read_waiting_TID != 0)
	{
		// wake the thread
		uint64_t t = msg_control->buffer_controls[index].client_read_waiting_TID;
		msg_control->buffer_controls[index].client_read_waiting_TID = 0;
		WakeThreadEx(t);
	}
	else
	{
		// no thread waiting, need to put self to sleep and await waking on next read
		msg_control->buffer_controls[index].client_write_sync_waiting_TID = GetCurrentTID();

		// release lock
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");

		// wait indefinitly
		WaitMe();

		// thread has woken, claim lock again and refresh CPU ID
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("cli");
		__asm__ __volatile__("":::"memory");
		CPU = GetCPUID();
		BakeryEnter(&(msg_control->buffer_controls[index].lock), CPU);

		// check if the thread was woken by something else (waiting thread value should be cleared)
		if(msg_control->buffer_controls[index].client_write_sync_waiting_TID != 0)
		{
			// if so, return error
			// release lock
			BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
			__asm__ __volatile__("":::"memory");
			__asm__ __volatile__("sti");
			__asm__ __volatile__("":::"memory");
			return 5;
		}
	}



	// release lock
	BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");

	// return success
	return 0;
}

// function for the server to send an async message to the client
// returns 0 on success, 1 if channel is not valid, 2 if calling PID is not the server, 3 if the client's buffer is full.
uint32_t ServerSendAsync(MSG_CHANNEL channel, uint8_t *buffer)
{
	// get channel index
	int index = channel & 0xffff;

	// aquire lock on the channel
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&(msg_control->buffer_controls[index].lock), CPU);

	// check channel exists
	// if not, error out
	if(msg_control->buffer_controls[index].channel_ID != channel)
	{
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 1;
	}

	// check the calling PID matches the server PID
	uint64_t PID = GetCurrentTID() >> 8;
	if(PID != msg_control->buffer_controls[index].server_ID)
	{
		// error out
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 2;
	}

	// get the index into the CLIENTS's receive queue
	int q_index = msg_control->buffer_controls[index].client_queue_start + msg_control->buffer_controls[index].client_message_count;

	// check the buffer is not full
	if(msg_control->buffer_controls[index].client_message_count == 64)
	{
		// error out
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 3;
	}

	// buffer is not full, increase message count
	msg_control->buffer_controls[index].client_message_count++;


	// put the message into the buffer
	uint64_t *input_buffer = (uint64_t*)buffer;
	uint64_t *dest_buffer = (uint64_t*)&(msg_buffers[index].client_queue[q_index].data);

	int i;
	for(i = 0; i < 32; i++)
	{
		dest_buffer[i] = input_buffer[i];
	}

	// check for thread waiting on a message
	if(msg_control->buffer_controls[index].client_read_waiting_TID != 0)
	{
		// wake the thread
		WakeThreadEx(msg_control->buffer_controls[index].client_read_waiting_TID);
		msg_control->buffer_controls[index].client_read_waiting_TID = 0;
	}

	// release lock
	BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");

	// return success
	return 0;
}

// message send functions
// returns 0 on success, 1 if channel is not valid, 2 if the calling PID is not the client, 3 if the buffer is full,
// 4 if there is already a thread waiting on a sync write, 5 if the thread was woken by something else
uint32_t ClientSendSync(MSG_CHANNEL channel, uint8_t *buffer, const uint64_t PID)
{
	// get channel index
	int index = channel & 0xffff;

	// aquire lock on the channel
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&(msg_control->buffer_controls[index].lock), CPU);

	// check channel exists
	// if not, error out
	if(msg_control->buffer_controls[index].channel_ID != channel)
	{
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 1;
	}

	// check the calling PID matches the server PID
	if(PID != msg_control->buffer_controls[index].client_ID)
	{
		// error out
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 2;
	}

	// check if no other thread is currently waiting on sync send
	if(msg_control->buffer_controls[index].server_write_sync_waiting_TID != 0)
	{
		// error out
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 4;
	}

	// get the index into the SERVER's receive queue
	int q_index = msg_control->buffer_controls[index].server_queue_start + msg_control->buffer_controls[index].server_message_count;

	// check the buffer is not full
	if(msg_control->buffer_controls[index].server_message_count == 64)
	{
		// error out
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 3;
	}

	// buffer is not full, increase message count
	msg_control->buffer_controls[index].server_message_count++;


	// put the message into the buffer
	uint64_t *input_buffer = (uint64_t*)buffer;
	uint64_t *dest_buffer = (uint64_t*)&(msg_buffers[index].server_queue[q_index].data);

	int i;
	for(i = 0; i < 32; i++)
	{
		dest_buffer[i] = input_buffer[i];
	}

	// check for thread waiting on a message
	if(msg_control->buffer_controls[index].server_read_waiting_TID != 0)
	{
		// wake the thread
		uint64_t t = msg_control->buffer_controls[index].server_read_waiting_TID;
		msg_control->buffer_controls[index].server_read_waiting_TID = 0;
		WakeThreadEx(t);
	}
	else
	{
		// no thread waiting, need to put self to sleep and await waking on next read
		msg_control->buffer_controls[index].server_write_sync_waiting_TID = GetCurrentTID();

		// release lock
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");

		// wait indefinitly
		WaitMe();

		// thread has woken, claim lock again and refresh CPU ID
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("cli");
		__asm__ __volatile__("":::"memory");
		CPU = GetCPUID();
		BakeryEnter(&(msg_control->buffer_controls[index].lock), CPU);

		// check if the thread was woken by something else (waiting thread value should be cleared)
		if(msg_control->buffer_controls[index].server_write_sync_waiting_TID != 0)
		{
			// if so, return error
			// release lock
			BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
			__asm__ __volatile__("":::"memory");
			__asm__ __volatile__("sti");
			__asm__ __volatile__("":::"memory");
			return 5;
		}
	}



	// release lock
	BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");

	// return success
	return 0;
}

// function for the client to send an async message to the server
// returns 0 on success, 1 if channel is not valid, 2 if calling PID is not the client, 3 if the server's buffer is full.
uint32_t ClientSendAsync(MSG_CHANNEL channel, uint8_t *buffer, uint64_t PID)
{
	// get channel index
	int index = channel & 0xffff;

	// aquire lock on the channel
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&(msg_control->buffer_controls[index].lock), CPU);

	// check channel exists
	// if not, error out
	if(msg_control->buffer_controls[index].channel_ID != channel)
	{
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 1;
	}

	// check the calling PID matches the client PID
	if(PID != msg_control->buffer_controls[index].client_ID)
	{
		// error out
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 2;
	}

	// get the index into the SERVER's receive queue
	int q_index = msg_control->buffer_controls[index].server_queue_start + msg_control->buffer_controls[index].server_message_count;

	// check the buffer is not full
	if(msg_control->buffer_controls[index].server_message_count == 64)
	{
		// error out
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 3;
	}

	// buffer is not full, increase message count
	msg_control->buffer_controls[index].server_message_count++;

	// put the message into the buffer
	uint64_t *input_buffer = (uint64_t*)buffer;
	uint64_t *dest_buffer = (uint64_t*)&(msg_buffers[index].server_queue[q_index].data);

	int i;
	for(i = 0; i < 32; i++)
	{
		dest_buffer[i] = input_buffer[i];
	}

	// check for thread waiting on a message
	if(msg_control->buffer_controls[index].server_read_waiting_TID != 0)
	{
		// wake the thread
		WakeThreadEx(msg_control->buffer_controls[index].server_read_waiting_TID);
		msg_control->buffer_controls[index].server_read_waiting_TID = 0;
	}

	// release lock
	BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");

	// return success
	return 0;
}

// functions for querying the number of pending messages in a buffer (can be called by anyone as long as the channel is correct)
// returns number of messages in queue. any errors return 0
uint32_t GetClientPendingMessages(MSG_CHANNEL channel)
{
	// get channel index
	int index = channel & 0xffff;

	// aquire lock on the channel
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&(msg_control->buffer_controls[index].lock), CPU);

	// check channel exists
	// if not, error out
	if(msg_control->buffer_controls[index].channel_ID != channel)
	{
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 0;
	}

	// channel exists, get message count
	int to_return = msg_control->buffer_controls[index].client_message_count;

	// release lock
	BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");

	// return
	return to_return;
}

uint32_t GetServerPendingMessages(MSG_CHANNEL channel)
{
	// get channel index
	int index = channel & 0xffff;

	// aquire lock on the channel
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&(msg_control->buffer_controls[index].lock), CPU);

	// check channel exists
	// if not, error out
	if(msg_control->buffer_controls[index].channel_ID != channel)
	{
		BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");
		return 0;
	}

	// channel exists, get message count
	int to_return = msg_control->buffer_controls[index].server_message_count;

	// release lock
	BakeryLeave(&(msg_control->buffer_controls[index].lock), CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");

	// return
	return to_return;
}


// function to deallocate all queues with the given server PID
void DeallocateForServerPID(uint64_t PID)
{
 	// scan through each of the channels
 	int i;
	for(i = 0; i < 512; i++)
	{
		// check if PIDs match
		if(msg_control->buffer_controls[i].server_ID == PID)
		{
			// deallocate channel
			// get lock on the channel
			__asm__ __volatile__("":::"memory");
			__asm__ __volatile__("cli");
			__asm__ __volatile__("":::"memory");
			int CPU = GetCPUID();
			BakeryEnter(&(msg_control->buffer_controls[i].lock), CPU);

			// zero server and client buffers
			msg_control->buffer_controls[i].client_message_count = 0;
			msg_control->buffer_controls[i].client_queue_last = 0;
			msg_control->buffer_controls[i].client_queue_start = 0;
			msg_control->buffer_controls[i].server_message_count = 0;
			msg_control->buffer_controls[i].server_queue_last = 0;
			msg_control->buffer_controls[i].server_queue_start = 0;

			// check each waiting value and wake the threads if they are non-zero
			if(msg_control->buffer_controls[i].client_read_waiting_TID != 0)
			{
				// wake the thread (don't need to check the return value)
				uint64_t TID_to_wake = msg_control->buffer_controls[i].client_read_waiting_TID;
				msg_control->buffer_controls[i].client_read_waiting_TID = 0;
				WakeThreadEx(TID_to_wake);
			}
			if(msg_control->buffer_controls[i].client_write_sync_waiting_TID != 0)
			{
				// wake the thread (don't need to check the return value)
				uint64_t TID_to_wake = msg_control->buffer_controls[i].client_write_sync_waiting_TID;
				msg_control->buffer_controls[i].client_write_sync_waiting_TID = 0;
				WakeThreadEx(TID_to_wake);
			}
			if(msg_control->buffer_controls[i].server_read_waiting_TID != 0)
			{
				// wake the thread (don't need to check the return value)
				uint64_t TID_to_wake = msg_control->buffer_controls[i].server_read_waiting_TID;
				msg_control->buffer_controls[i].server_read_waiting_TID = 0;
				WakeThreadEx(TID_to_wake);
			}
			if(msg_control->buffer_controls[i].server_write_sync_waiting_TID != 0)
			{
				// wake the thread (don't need to check the return value)
				uint64_t TID_to_wake = msg_control->buffer_controls[i].server_write_sync_waiting_TID;
				msg_control->buffer_controls[i].server_write_sync_waiting_TID = 0;
				WakeThreadEx(TID_to_wake);
			}

			// zero owner IDs and channel ID
			msg_control->buffer_controls[i].channel_ID = 0;
			msg_control->buffer_controls[i].client_ID = 0;
			msg_control->buffer_controls[i].server_ID = 0;

			// unlock buffer controls
			BakeryLeave(&(msg_control->buffer_controls[i].lock), CPU);

			// place buffer on stack
			BakeryEnter(&(msg_control->stack_lock), CPU);

			msg_control->buffer_stack_index--;
			msg_control->buffer_stack[msg_control->buffer_stack_index] = i;

			// release lock
			BakeryLeave(&(msg_control->stack_lock), CPU);
			__asm__ __volatile__("":::"memory");
			__asm__ __volatile__("sti");
			__asm__ __volatile__("":::"memory");
		}
	}

 	// finished
 	return;
}





















